/* Copyright (c) 2006 Tomasz Michal Lukaszewski
 */
#ifndef	serializer_h_
# define serializer_h_

#include <string>
#include <cstring>
#include <memory>
#include <stdexcept>


#ifdef __CYGWIN__
# define LITTLE_ENDIAN 1234
# define BIG_ENDIAN 4321
# define BYTE_ORDER LITTLE_ENDIAN	/* Cygwin is always little-endian */
#else
# ifndef LITTLE_ENDIAN
#  define LITTLE_ENDIAN 1234
# endif
# ifndef BIG_ENDIAN
#  define BIG_ENDIAN 1234
# endif
# ifdef __LITTLE_ENDIAN__		/* defined on MacOSX/i386 */
#  undef BYTE_ORDER
#  define BYTE_ORDER LITTLE_ENDIAN
# endif
# ifdef __BIG_ENDIAN__			/* defined on MacOSX/ppc */
#  undef BYTE_ORDER
#  define BYTE_ORDER BIG_ENDIAN
# endif
# ifndef BYTE_ORDER
#  include <endian.h>			/* available on Linux */
# endif
#endif


#ifdef BYTE_ORDER
# if BYTE_ORDER == LITTLE_ENDIAN
#  define swap16(x) (x)
#  define swap32(x) (x)
# elif BYTE_ORDER == BIG_ENDIAN
#  define swap16(x) ((((x) >> 8) & 0xff) | (((x) & 0xff) << 8))
#  define swap32(x) ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >>  8) |\
		(((x) & 0x0000ff00) <<  8) | (((x) & 0x000000ff) << 24))
# else
#  error "Unknown BYTE_ORDER value!"
# endif
#else
# define swap16(x) (x)
# define swap32(x) (x)
#endif

class serializer {
private:
	u_int8_t* image;
	u_int32_t length;

	u_int8_t *pos;
	u_int32_t offs;

public:
	serializer(u_int8_t* const image, u_int32_t length)
	{
		this->image = image;
		this->length = length;
		this->pos = image;
		this->offs = 0;
	}

	serializer(serializer& orig)
	{
		this->image = orig.image;
		this->length = orig.length;
		this->pos = orig.pos;
		this->offs = orig.offs;
	}

	~serializer() { }


	u_int32_t get_pos() const { return offs; }

	void rewind()
	{
		pos = image;
		offs = 0;
	}

	void advance(int32_t adv)
	{
		int64_t new_offs = (int64_t) offs + adv;
		if (new_offs < 0) {
			offs = 0;
			pos = image;
		} else if (new_offs <= length) {
			offs = new_offs;
			pos += adv;
		} else {
			throw std::runtime_error("new position beyond EOF");
		}
	}

	serializer& operator<<(u_int8_t x)
	{
		if (offs == length)
			throw std::runtime_error("write overflow");
		*pos++ = x;
		offs++;

		return *this;
	}

	serializer& operator<<(u_int16_t x)
	{
		if (offs + 2 > length)
			throw std::runtime_error("write overflow");
		*((u_int16_t *) pos) = swap16(x);
		pos += 2;
		offs += 2;

		return *this;
	}

	serializer& operator<<(int32_t x)
	{
		if (offs + 4 > length)
			throw std::runtime_error("write overflow");
		*((int32_t *) pos) = swap32(x);
		pos += 4;
		offs += 4;

		return *this;
	}

	serializer& operator<<(u_int32_t x)
	{
		if (offs + 4 > length)
			throw std::runtime_error("write overflow");
		*((u_int32_t *) pos) = swap32(x);
		pos += 4;
		offs += 4;

		return *this;
	}

	serializer& operator<<(const std::string& str)
	{
		if (offs + str.length() + 1 > length)
			throw std::runtime_error("write overflow");
		strcpy((char*) pos, str.c_str());
		pos += str.length() + 1;
		offs += str.length() + 1;

		return *this;
	}

/*
	template<typename T>
	serializer& operator<<(T& x)
	{
		if (offs + sizeof(T) > length)
			throw std::runtime_error("write overflow");
		memcpy(pos, &x, sizeof(T));
		pos += sizeof(T);
		offs += sizeof(T);

		return *this;
	}
 */

	serializer& operator>>(u_int8_t& x)
	{
		if (offs == length)
			throw std::runtime_error("read beyond EOF");
		x = *pos++;
		offs++;

		return *this;
	}

	serializer& operator>>(u_int16_t& x)
	{
		if (offs + 2 > length)
			throw std::runtime_error("read beyond EOF");
		x = swap16(*((u_int16_t *) pos));
		pos += 2;
		offs += 2;

		return *this;
	}

	serializer& operator>>(int32_t& x)
	{
		if (offs + 4 > length)
			throw std::runtime_error("read beyond EOF");
		x = swap32(*((int32_t *) pos));
		pos += 4;
		offs += 4;

		return *this;
	}

	serializer& operator>>(u_int32_t& x)
	{
		if (offs + 4 > length)
			throw std::runtime_error("read beyond EOF");
		x = swap32(*((u_int32_t *) pos));
		pos += 4;
		offs += 4;

		return *this;
	}

	serializer& operator>>(std::string& str)
	{
		u_int8_t *p = pos;
		u_int32_t x = offs;

		for (; x <= length && *p != '\0'; x++, p++);
		if (*p != '\0')
			throw std::runtime_error("read beyond EOFa");
		str.assign((const char*) pos);
		pos += str.length() + 1;
		offs += str.length() + 1;

		return *this;
	}

/*
	template<typename T>
	serializer& operator>>(T& x)
	{
		if (offs + sizeof(T) > length)
			throw std::runtime_error("read beyond EOF");
		memcpy(&x, pos, sizeof(T));
		pos += sizeof(T);
		offs += sizeof(T);

		return *this;
	}
 */
};

#endif	// serializer_h_
